Add new format "garmin_txt".
authoroliskoli <oliskoli@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Wed, 5 Apr 2006 17:48:05 +0000 (17:48 +0000)
committeroliskoli <oliskoli@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Wed, 5 Apr 2006 17:48:05 +0000 (17:48 +0000)
gpsbabel/garmin_txt.c [new file with mode: 0644]
gpsbabel/reference/garmin_txt.txt [new file with mode: 0644]
gpsbabel/reference/gdb-sample2.gdb [new file with mode: 0644]

diff --git a/gpsbabel/garmin_txt.c b/gpsbabel/garmin_txt.c
new file mode 100644 (file)
index 0000000..072f7c5
--- /dev/null
@@ -0,0 +1,1378 @@
+/*
+
+    Support for MapSource Text Export (Tab delimited) files.
+    
+    Copyright (C) 2006 Olaf Klein, o.b.klein@t-online.de
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+#include "defs.h"
+
+#include <math.h>
+#include <time.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include "cet_util.h"
+#include "csv_util.h"
+#include "garmin_fs.h"
+#include "garmin_tables.h"
+#include "grtcirc.h"
+#include "inifile.h"
+#include "jeeps/gpsmath.h"
+#include "strptime.h"
+
+#define MYNAME "garmin_txt"
+
+typedef struct gtxt_flags_s {
+       unsigned int metric:1;
+       unsigned int celsius:1;
+       unsigned int utc:1;
+       unsigned int enum_waypoints:1;
+       unsigned int route_header_written:1;
+       unsigned int track_header_written:1;
+} gtxt_flags_t;
+
+static FILE *fin, *fout;
+static route_head *current_trk, *current_rte;
+static int waypoints;
+static int routepoints;
+static waypoint **wpt_a;
+static int wpt_a_ct;
+static int grid_index;
+static int datum_index;
+static char *datum_str;
+static int current_line;
+static char *date_time_format = NULL;
+static int precision = 3;
+static time_t utc_offs = 0;
+
+static gtxt_flags_t gtxt_flags;
+
+typedef enum {
+       waypt_header = 0,
+       rtept_header,
+       trkpt_header,
+       route_header,
+       track_header,
+       unknown_header
+} header_type;
+
+#define MAX_HEADER_FIELDS 24
+
+static char *header_lines[unknown_header + 1][MAX_HEADER_FIELDS];
+static int header_fields[unknown_header + 1][MAX_HEADER_FIELDS];
+static int header_ct[unknown_header + 1];
+
+#define GARMIN_UNKNOWN_ALT 1.0e25f
+#define DEFAULT_DISPLAY garmin_display_symbol_and_name
+#define DEFAULT_DATE_FORMAT "dd/mm/yyyy"
+#define DEFAULT_TIME_FORMAT "HH:mm:ss"
+
+/* macros */
+
+#define IS_VALID_ALT(a) (((a) != unknown_alt) && ((a) < GARMIN_UNKNOWN_ALT))
+#define DUPSTR(a) (((a) != NULL) && ((a)[0] != 0)) ? xstrdup((a)) : NULL
+
+#define mod(y,x) (y) - (x) * floor((y)/(x))
+#define d2r(a) ((double)((a) * M_PI) / (double)180.0)
+#define r2d(a) ((a) * (double)180 / M_PI)
+
+static char *opt_datum = NULL;
+static char *opt_dist = NULL;
+static char *opt_temp = NULL;
+static char *opt_date_format = NULL;
+static char *opt_time_format = NULL;
+static char *opt_precision = NULL;
+static char *opt_utc = NULL;
+
+static
+arglist_t garmin_txt_args[] = {
+       {"date",  &opt_date_format, "Read/Write date format (i.e. yyyy/mm/dd)", NULL, ARGTYPE_STRING, ARG_NOMINMAX}, 
+       {"datum", &opt_datum,       "GPS datum (def. WGS 84)", "WGS 84", ARGTYPE_STRING, ARG_NOMINMAX}, 
+       {"dist",  &opt_dist,        "Distance unit [m=metric, s=statue]", "m", ARGTYPE_STRING, ARG_NOMINMAX},
+       {"prec",  &opt_precision,   "Precision of coordinates", "3", ARGTYPE_INT, ARG_NOMINMAX},
+       {"temp",  &opt_temp,        "Temperature unit [c=celsius, f=fahrenheit]", "c", ARGTYPE_STRING, ARG_NOMINMAX}, 
+       {"time",  &opt_time_format, "Read/Write time format (i.e. HH:mm:ss xx)", NULL, ARGTYPE_STRING, ARG_NOMINMAX},
+       {"utc",   &opt_utc,         "Write timestamps with offset x to UTC time", NULL, ARGTYPE_INT, "-23", "+23"},
+       ARG_TERMINATOR
+};
+
+typedef struct info_s
+{
+       double length;
+       time_t start;
+       time_t time;
+       double speed;
+       double total;
+       int count;
+       waypoint *prev_wpt;
+       waypoint *first_wpt;
+       waypoint *last_wpt;
+} info_t;
+
+static info_t *route_info;
+static int route_idx;
+static info_t *cur_info;
+
+static char *headers[] = {
+       "Name\tDescription\tType\tPosition\tAltitude\tDepth\tProximity\tTemperature\t"
+               "Display Mode\tColor\tSymbol\tFacility\tCity\tState\tCountry\t"
+               "Date Modified\tLink\tCategories",
+       "Waypoint Name\tDistance\tLeg Length\tCourse",
+       "Position\tTime\tAltitude\tDepth\tLeg Length\tLeg Time\tLeg Speed\tLeg Course",
+       "Name\tLength\tCourse\tWaypoints\tLink",
+       "Name\tStart Time\tElapsed Time\tLength\tAverage Speed\tLink",
+       NULL
+};
+
+/* helpers */
+
+static char *
+get_option_val(char *option, char *def)
+{
+       char *c = (option != NULL) ? option : def;
+       return c;
+}
+
+static void
+init_date_and_time_format(void)
+{
+       struct tm tm;
+       char *c, *origin, *src, *dest, prev;
+       char buff[64], timef[32], datef[32];
+       static char format[128];
+       int offs, Y, H;
+       time_t time;
+       
+       memset(&datef, 0, sizeof(datef));
+       memset(&timef, 0, sizeof(timef));
+       
+       origin = get_option_val(opt_date_format, DEFAULT_DATE_FORMAT);
+       strncpy(buff, origin, sizeof(buff));
+       
+       src = lrtrim(buff);
+       for (c = src; *c; c++) *c = toupper(*c);
+       
+       Y = 0;
+       prev = '\0';
+       offs = (src - buff);
+       dest = datef;
+       
+       for (c = src; *c; c++, offs++) {
+               if (isalpha(*c)) {
+                       switch(*c) {
+                       case 'J':
+                       case 'Y':
+                               if (prev != 'Y') { 
+                                       strcat(dest, "%y"); 
+                                       dest += 2;
+                                       prev = 'Y'; 
+                               }
+                               Y++;
+                               if (Y > 2) *(dest-1) = 'Y';
+                               break;
+                       case 'M':
+                               if (prev != 'M') {
+                                       strcat(dest, "%m");
+                                       dest += 2;
+                                       prev = 'M';
+                               }
+                               break;
+                       case 'D':
+                       case 'T': 
+                               if (prev != 'D') {
+                                       strcat(dest, "%d");
+                                       dest += 2;
+                                       prev = 'D';
+                               }
+                               break;
+                       default:
+                               fatal(MYNAME ": Invalid character \"%c\" in date format!\n", origin[offs]);
+                       }
+               }
+               else if (ispunct(*c)) {
+                       *dest++ = *c;
+               }
+       }
+       
+       origin = get_option_val(opt_time_format, DEFAULT_TIME_FORMAT);
+       strncpy(buff, origin, sizeof(buff));
+       
+       src = lrtrim(buff);
+       H = 0;
+       prev = '\0';
+       offs = (src - buff);
+       dest = timef;
+       
+       for (c = src; *c; c++, offs++) {
+               if (isalpha(*c)) {
+                       switch(*c) {
+                       case 'S':
+                       case 's':
+                               if (prev != 'S') { 
+                                       strcat(dest, "%S"); 
+                                       dest += 2;
+                                       prev = 'S'; 
+                               }
+                               break;
+                       case 'M':
+                       case 'm':
+                               if (prev != 'M') { 
+                                       strcat(dest, "%M"); 
+                                       dest += 2;
+                                       prev = 'M'; 
+                               }
+                               break;
+                       case 'h':       /* 12-hour-clock */
+                               if (prev != 'H') {
+                                       strcat(dest, "%l");
+                                       dest += 2;
+                                       prev = 'H';
+                               }
+                               else *(dest-1) = 'I';
+                               break;
+                       case 'H':       /* 24-hour-clock */
+                               if (prev != 'H') {
+                                       strcat(dest, "%l");
+                                       dest += 2;
+                                       prev = 'H';
+                               }
+                               else *(dest-1) = 'H';
+                               break;
+                       case 'x':
+                               if (prev != 'X') {
+                                       strcat(dest, "%P");
+                                       dest += 2;
+                                       prev = 'X';
+                               }
+                               else *(dest-1) = 'P';
+                               break;
+                       case 'X':
+                               if (prev != 'X') {
+                                       strcat(dest, "%p");
+                                       dest += 2;
+                                       prev = 'X';
+                               }
+                               else *(dest-1) = 'p';
+                               break;
+                       default:
+                               fatal(MYNAME ": Invalid character \"%c\" in time format!\n", origin[offs]);
+                       }
+               }
+               else *dest++ = *c;
+       }
+       
+       strcpy(format, datef);
+       strcat(format, " ");
+       strcat(format, timef);
+       date_time_format = format;
+}
+
+static double
+distance(double lat1, double lon1, double lat2, double lon2)   /* nearly MapSource compatible */
+{
+       return r2d(gcdist(d2r(lat1), d2r(lon1), d2r(lat2), d2r(lon2))) * 111.32 * 1000.0;
+}
+
+static double
+course_rad(double lat1, double lon1, double lat2, double lon2)
+{
+       return mod(atan2(sin(lon1 - lon2) * cos(lat2), cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1-lon2)), 2 * M_PI);
+}
+
+static double
+course_deg(double lat1, double lon1, double lat2, double lon2)
+{
+       return r2d(course_rad(d2r(lat1), d2r(lon1), d2r(lat2), d2r(lon2)));
+}
+
+static double
+waypt_distance(const waypoint *A, const waypoint *B)           /* !!! from A to B !!! */
+{
+       double dist = 0;
+       garmin_fs_p gmsd;
+       
+       if ((A == NULL) || (B == NULL)) return 0;
+       
+       gmsd = GMSD_FIND(A);
+       if ((gmsd != NULL) && (gmsd->ilinks != NULL))
+       {
+               garmin_ilink_t *link = gmsd->ilinks;
+               
+               dist = distance(A->latitude, A->longitude, link->lat, link->lon);
+               while (link->next != NULL)
+               {
+                       garmin_ilink_t *prev = link;
+                       link = link->next;
+                       dist += distance(prev->lat, prev->lon, link->lat, link->lon);
+               }
+               dist += distance(link->lat, link->lon, B->latitude, B->longitude);
+       } else 
+       {
+               dist = distance(A->latitude, A->longitude, B->latitude, B->longitude);
+       }
+       return dist;
+}
+
+static void
+convert_datum(waypoint *wpt, const int to_internal_wgs84, double *dest_lat, double *dest_lon)
+{
+       double alt;
+
+       if (datum_index == 118 /* WGS 84 */) {
+               if (to_internal_wgs84 == 0) {
+                       *dest_lat = wpt->latitude;
+                       *dest_lon = wpt->longitude;
+               }
+               return;
+       }
+               
+       if (to_internal_wgs84) { /* convert the waypoint himself */
+               GPS_Math_Known_Datum_To_WGS84_M(wpt->latitude, wpt->longitude, 0.0,
+                       &wpt->latitude, &wpt->longitude, &alt, datum_index);
+       } else
+               GPS_Math_WGS84_To_Known_Datum_M(wpt->latitude, wpt->longitude, 0.0,
+                       dest_lat, dest_lon, &alt, datum_index);
+}
+
+/* WRITER *****************************************************************/
+
+/* Waypoint preparation */
+
+static void
+enum_waypt_cb(const waypoint *wpt)
+{
+       garmin_fs_p gmsd;
+       int wpt_class;
+       
+       gmsd = GMSD_FIND(wpt); 
+       wpt_class = GMSD_GET(wpt_class, 0);
+       if (wpt_class < 0x80)
+       {
+               int i;
+               
+               if (gtxt_flags.enum_waypoints)                  /* enumerate only */
+               {
+                       waypoints++;
+                       return;
+               }
+               for (i = 0; i < wpt_a_ct; i++) {                /* check for duplicates */
+                       waypoint *tmp = wpt_a[i];
+                       if (case_ignore_strcmp(tmp->shortname, wpt->shortname) == 0)
+                       {
+                               wpt_a[i] = (waypoint *)wpt;
+                               waypoints--;
+                               return;
+                               
+                       }
+               }
+               wpt_a[wpt_a_ct++] = (waypoint *)wpt;
+       }
+
+}
+
+static int
+sort_waypt_cb(const void *a, const void *b)
+{
+       const waypoint *wa = *(waypoint **)a;
+       const waypoint *wb = *(waypoint **)b;
+
+       return case_ignore_strcmp(wa->shortname, wb->shortname);
+}
+
+
+/* common route and track pre-work */
+
+static void
+prework_hdr_cb(const route_head *rte)
+{
+       cur_info = &route_info[route_idx];
+       cur_info->prev_wpt = NULL;
+       cur_info->length = 0;
+       cur_info->time = 0;
+}
+
+static void
+prework_tlr_cb(const route_head *rte)
+{
+       cur_info->last_wpt = cur_info->prev_wpt;
+       route_idx++;
+}
+
+static void
+prework_wpt_cb(const waypoint *wpt)
+{
+       waypoint *prev = cur_info->prev_wpt;
+
+       if (prev != NULL) {
+               cur_info->time += (wpt->creation_time - prev->creation_time);
+               cur_info->length += waypt_distance(prev, wpt);
+       }
+       else {
+               cur_info->first_wpt = (waypoint *)wpt;
+               cur_info->start = wpt->creation_time;
+       }
+       cur_info->prev_wpt = (waypoint *)wpt;
+       cur_info->count++;
+       routepoints++;
+}
+
+
+/* output helpers */
+
+static void
+print_position(const waypoint *wpt)
+{
+       int deg;
+       double min;
+       char num[64];
+       double lat, lon;
+       
+       convert_datum((waypoint *)wpt, 0, &lat, &lon);
+
+       deg = fabs(lat);
+       min = (double)60.0 * (fabs(lat) - deg);
+       snprintf(num, sizeof(num), "%0*.*f", precision + 3, precision, min);
+       if (atoi(num) == 60) {
+               deg++;
+               min = 0;
+       }
+       fprintf(fout, "%c%d %0*.*f ", lat < 0.0 ? 'S' : 'N', deg, precision + 3, precision, min);
+       
+       deg = fabs(lon);
+       min = (double)60.0 * (fabs(lon) - deg);
+       snprintf(num, sizeof(num), "%0*.*f", precision + 3, precision, min);
+       if (atoi(num) == 60) {
+               deg++;
+               min = 0;
+       }
+       fprintf(fout, "%c%d %0*.*f\t", lon < 0.0 ? 'W' : 'E', deg, precision + 3,  precision, min);
+}
+
+static void
+print_date_and_time(const time_t time, const int time_only)
+{
+       struct tm tm, tm2;
+       char tbuf[32];
+       
+       if (time_only) {
+               tm = *gmtime(&time);
+               snprintf(tbuf, sizeof(tbuf), "%d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
+               fprintf(fout, "%s", tbuf);
+       }
+       else if (time != 0) {
+               if (gtxt_flags.utc) {
+                       time_t t = time + utc_offs;
+                       tm = *gmtime(&t);
+               }
+               else
+                       tm = *localtime(&time);
+               strftime(tbuf, sizeof(tbuf), date_time_format, &tm);
+               fprintf(fout, "%s ", tbuf);
+       }
+       fprintf(fout, "\t");
+}
+
+static void
+print_categories(gbuint16 categories)
+{
+       int i, count;
+       char *c;
+       
+       if (categories == 0) return;
+       
+       count = 0;
+       for (i = 0; i < 16; i++) {
+               if ((categories & 1) != 0) {
+                       if (global_opts.inifile != NULL) {
+                               char key[3];
+                               snprintf(key, sizeof(key), "%d", i + 1);
+                               c = inifile_readstr(global_opts.inifile, GMSD_SECTION_CATEGORIES, key);
+                       }
+                       else c = NULL;
+                               
+                       fprintf(fout, "%s", (count++ > 0) ? "," : "");
+                       if (c == NULL)
+                               fprintf(fout, "Category %d", i+1);
+                       else
+                               fprintf(fout, "%s", c);
+                       
+               }
+               categories = categories >> 1;
+       }
+}
+
+static void
+print_course(const waypoint *A, const waypoint *B)             /* seems to be okay */
+{
+       if ((A != NULL) && (B != NULL) && (A != B)) {
+               int course;
+               char buff[32];
+               course = si_round((double)360 - course_deg(A->latitude, A->longitude, B->latitude, B->longitude));
+               if (course >= 360) {
+                       course -= 360;
+               }
+               cet_fprintf(fout, &cet_cs_vec_cp1252, "%d%c true", course, 0xB0);
+       }
+}
+
+static void
+print_distance(const double distance, const int no_scale, const int with_tab)
+{
+       double dist = distance;
+       
+       if (gtxt_flags.metric == 0) {
+               dist = dist / (double)0.3048;
+       
+               if ((dist < 5280) || no_scale)
+                       fprintf(fout, "%.f ft", dist);
+               else {
+                       dist = (distance / 1609.344);
+                       if (dist < (double)100)
+                               fprintf(fout, "%.1f mi", dist);
+                       else
+                               fprintf(fout, "%d mi", si_round(dist));
+               }
+       }
+       else
+       {
+               if ((dist < 1000) || no_scale)
+                       fprintf(fout, "%.f m", dist);
+               else {
+                       dist = dist / (double)1000.0;
+                       if (dist < (double)100)
+                               fprintf(fout, "%.1f km", dist);
+                       else
+                               fprintf(fout, "%d km", si_round(dist));
+               }
+       }
+       if (with_tab) fprintf(fout, "\t");
+}
+
+static void
+print_speed(double *distance, time_t *time)
+{
+       int idist;
+       double dist = *distance;
+       char *unit;
+
+       if (!gtxt_flags.metric) {
+               dist = dist / (double)1.609344;
+               unit = "mph";
+       }
+       else unit = "kph";
+       idist = si_round(dist);
+       
+       if ((time != 0) && (idist > 0)) {
+               double speed = dist / (double)*time * (double)3.600;
+               int ispeed = si_round(speed);
+               
+               if (speed < (double)0.01)
+                       fprintf(fout, "0 %s", unit);
+               else if (ispeed < 2)
+                       fprintf(fout, "%.1f %s", speed, unit);
+               else
+                       fprintf(fout, "%d %s", ispeed, unit);
+       }
+       else
+               fprintf(fout, "0 %s", unit);
+       fprintf(fout, "\t");
+}
+
+
+/* main cb's */
+
+static void
+write_waypt(const waypoint *wpt)
+{
+       unsigned char wpt_class;
+       garmin_fs_p gmsd;
+       char *wpt_type;
+       char *dspl_mode;
+       char *country;
+       double x;
+       int i, icon, dynamic;
+       char *icon_descr;
+       
+       gmsd = GMSD_FIND(wpt);
+
+       i = GMSD_GET(display, 0);
+       if (i > GT_DISPLAY_MODE_MAX) i = 0;
+       dspl_mode = gt_display_mode_names[i];
+
+       wpt_class = GMSD_GET(wpt_class, 0);
+       if (wpt_class <= gt_waypt_class_map_line)
+               wpt_type = gt_waypt_class_names[wpt_class];
+       else
+               wpt_type = gt_waypt_class_names[0];
+       
+       fprintf(fout, "Waypoint\t%s\t", (wpt->shortname) ? wpt->shortname : "");
+       if (wpt_class <= gt_waypt_class_airport_ndb) {
+               char *temp = wpt->notes;
+               if (temp == NULL) {
+                       if (wpt->description && (strcmp(wpt->description, wpt->shortname) != 0))
+                               temp = wpt->description;
+                       else
+                               temp = "";
+               }
+               fprintf(fout, "%s\t", temp);
+       }
+       else
+               fprintf(fout, "\t");
+       fprintf(fout, "%s\t", wpt_type);
+               
+       print_position(wpt);
+       
+       if IS_VALID_ALT(wpt->altitude)
+               print_distance(wpt->altitude, 1, 0);
+       fprintf(fout, "\t");
+       
+       x = GMSD_GET(depth, unknown_alt);
+       if (x != unknown_alt)
+               print_distance(x, 1, 0);
+       fprintf(fout, "\t");
+
+       x = GMSD_GET(proximity, unknown_alt);
+       if (x != unknown_alt)
+               print_distance(x, 0, 0);
+       fprintf(fout, "\t");
+       
+       x = GMSD_GET(temperature, unknown_alt);
+       if (x != unknown_alt) {
+               if (gtxt_flags.celsius)
+                       fprintf(fout, "%.f C", x);
+               else
+                       fprintf(fout, "%.f F", (x * 1.8) + 32);
+       }
+       fprintf(fout, "\t%s\t", dspl_mode);
+       
+       fprintf(fout, "Unknown\t");                             /* Color is fixed: Unknown */
+
+       icon = GMSD_GET(icon, -1);
+       if (icon == -1) {
+               icon = gt_find_icon_number_from_desc(wpt->icon_descr, GDB);
+       }
+       icon_descr = gt_find_desc_from_icon_number(icon, GDB, &dynamic);
+       fprintf(fout, "%s\t", icon_descr);
+       if (dynamic) xfree(icon_descr);
+       
+       fprintf(fout, "%s\t", GMSD_GET(facility, ""));
+       fprintf(fout, "%s\t", GMSD_GET(city, ""));
+       fprintf(fout, "%s\t", GMSD_GET(state, ""));
+       country = gt_get_icao_country(GMSD_GET(cc, ""));
+       fprintf(fout, "%s\t", (country != NULL) ? country : "");
+       print_date_and_time(wpt->creation_time, 0);
+       fprintf(fout, "%s\t", wpt->url ? wpt->url : ""); 
+       print_categories(GMSD_GET(category, 0));
+       
+       fprintf(fout, "\r\n");
+}
+
+static void
+route_disp_hdr_cb(const route_head *rte)
+{
+       current_trk = (route_head *)rte;
+       cur_info = &route_info[route_idx];
+       cur_info->prev_wpt = NULL;
+       cur_info->total = 0;
+       if (rte->rte_waypt_ct <= 0) return;
+
+       if (!gtxt_flags.route_header_written) {
+               gtxt_flags.route_header_written = 1;
+               fprintf(fout, "\r\n\r\nHeader\t%s\r\n", headers[route_header]);
+       }
+
+       fprintf(fout, "\r\nRoute\t%s\t", current_trk->rte_name ? current_trk->rte_name : "");
+       print_distance(cur_info->length, 0, 1);
+       print_course(cur_info->first_wpt, cur_info->last_wpt);
+       fprintf(fout, "\t%d waypoints\t", cur_info->count);
+       fprintf(fout, "%s\r\n", rte->rte_url ? rte->rte_url : "");
+       fprintf(fout, "\r\nHeader\t%s\r\n\r\n", headers[rtept_header]);
+}
+
+static void
+route_disp_tlr_cb(const route_head *rte)
+{
+       route_idx++;
+}
+
+static void
+route_disp_wpt_cb(const waypoint *wpt)
+{
+       waypoint *prev = cur_info->prev_wpt;
+       
+       fprintf(fout, "Route Waypoint\t");
+       fprintf(fout, "%s\t", wpt->shortname);
+
+       if (prev != NULL)
+       {
+               double dist = waypt_distance(prev, wpt);
+               cur_info->total += dist;
+               print_distance(cur_info->total, 0, 1);
+               print_distance(dist, 0, 1);
+               print_course(prev, wpt);
+       }
+       else 
+               print_distance(0, 1, 0);        
+
+       fprintf(fout, "\r\n");
+       
+       cur_info->prev_wpt = (waypoint *)wpt;
+}
+
+static void
+track_disp_hdr_cb(const route_head *track)
+{
+       cur_info = &route_info[route_idx];
+       cur_info->prev_wpt = NULL;
+       cur_info->total = 0;
+       current_trk = (route_head *)track;
+       if (track->rte_waypt_ct <= 0) return;
+       
+       if (!gtxt_flags.track_header_written) {
+               gtxt_flags.track_header_written = 1;
+               fprintf(fout, "\r\n\r\nHeader\t%s\r\n", headers[track_header]);
+       }
+               
+       fprintf(fout, "\r\nTrack\t%s\t", current_trk->rte_name ? current_trk->rte_name : "");
+       print_date_and_time(cur_info->start, 0);
+       print_date_and_time(cur_info->time, 1);
+       print_distance(cur_info->length, 0, 1);
+       print_speed(&cur_info->length, &cur_info->time);
+       fprintf(fout, "\r\n\r\nHeader\t%s\r\n\r\n", headers[trkpt_header]);
+}
+
+static void
+track_disp_tlr_cb(const route_head *track)
+{
+       route_idx++;
+}
+
+static void
+track_disp_wpt_cb(const waypoint *wpt)
+{
+       waypoint *prev = cur_info->prev_wpt;
+       time_t delta;
+       double dist;
+       
+       fprintf(fout, "Trackpoint\t");
+
+       print_position(wpt);
+       print_date_and_time(wpt->creation_time, 0);
+       if IS_VALID_ALT(wpt->altitude)
+               print_distance(wpt->altitude, 1, 0);
+       fprintf(fout, "\t0.0 %s", (gtxt_flags.metric) ? "m" : "ft");
+       if (prev != NULL) {
+               fprintf(fout, "\t");
+               delta = wpt->creation_time - prev->creation_time;
+               dist = distance(prev->latitude, prev->longitude, wpt->latitude, wpt->longitude);
+               print_distance(dist, 0, 1);
+               print_date_and_time(delta, 1);
+               print_speed(&dist, &delta);
+               print_course(prev, wpt);
+       }
+       fprintf(fout, "\r\n");
+       
+       cur_info->prev_wpt = (waypoint *)wpt;
+}
+
+/*******************************************************************************
+* %%%        global callbacks called by gpsbabel main process              %%% *
+*******************************************************************************/
+
+static void
+garmin_txt_wr_init(const char *fname)
+{
+       char *temp;
+       
+       memset(&gtxt_flags, 0, sizeof(gtxt_flags));
+       
+       fout = xfopen(fname, "wb", MYNAME);
+       grid_index = 1;
+       
+       gtxt_flags.metric = (toupper(*get_option_val(opt_dist, "m")) == 'M');
+       gtxt_flags.celsius = (toupper(*get_option_val(opt_temp, "c")) == 'C');
+       init_date_and_time_format();
+       if (opt_precision) {
+               precision = atoi(opt_precision);
+               is_fatal(precision < 0, MYNAME ": Invalid precision (%s)!", opt_precision);
+       }
+       datum_str = get_option_val(opt_datum, NULL);
+       datum_index = GPS_Lookup_Datum_Index(datum_str);
+       is_fatal(datum_index < 0, MYNAME ": Invalid or unknown gps datum (%s)!", datum_str);
+       
+       if (opt_utc != NULL) {
+               if (case_ignore_strcmp(opt_utc, "utc") == 0)
+                       utc_offs = 0;
+               else
+                       utc_offs = atoi(opt_utc);
+               utc_offs *= (60 * 60);
+               gtxt_flags.utc = 1;
+       }
+}
+
+static void
+garmin_txt_wr_deinit(void)
+{
+       fclose(fout);
+}
+
+static void
+garmin_txt_write(void)
+{
+       cet_fprintf(fout, &cet_cs_vec_cp1252, "Grid\tLat/Lon hddd%cmm.mmm'\r\n", 0xB0);
+       fprintf(fout, "Datum\t%s\r\n\r\n", datum_str);
+
+       waypoints = 0;
+       gtxt_flags.enum_waypoints = 1;                  /* enum all waypoints */
+       waypt_disp_all(enum_waypt_cb);
+       route_disp_all(NULL, NULL, enum_waypt_cb);
+       gtxt_flags.enum_waypoints = 0;
+       
+       if (waypoints > 0) {
+               int i;
+               
+               wpt_a_ct = 0;
+               wpt_a = (waypoint **)xcalloc(waypoints, sizeof(*wpt_a));
+               waypt_disp_all(enum_waypt_cb);
+               route_disp_all(NULL, NULL, enum_waypt_cb);
+               qsort(wpt_a, waypoints, sizeof(*wpt_a), sort_waypt_cb);
+               
+               fprintf(fout, "Header\t%s\r\n\r\n", headers[waypt_header]);
+               for (i = 0; i < waypoints; i++)
+               {
+                       waypoint *wpt = wpt_a[i];
+                       write_waypt(wpt);
+               }
+               xfree(wpt_a);
+               
+               route_idx = 0;
+               route_info = xcalloc(route_count(), sizeof(struct info_s));
+               routepoints = 0;
+               route_disp_all(prework_hdr_cb, prework_tlr_cb, prework_wpt_cb);
+               if (routepoints > 0)
+               {
+                       route_idx = 0;
+                       route_disp_all(route_disp_hdr_cb, route_disp_tlr_cb, route_disp_wpt_cb);
+               }
+               xfree(route_info);
+       }
+
+       route_idx = 0;
+       route_info = xcalloc(track_count(), sizeof(struct info_s));
+       routepoints = 0;
+       track_disp_all(prework_hdr_cb, prework_tlr_cb, prework_wpt_cb);
+               
+       if (routepoints > 0) {
+               route_idx = 0;
+               track_disp_all(track_disp_hdr_cb, track_disp_tlr_cb, track_disp_wpt_cb);
+       }
+       xfree(route_info);
+}
+
+/* READER *****************************************************************/
+
+/* helpers */
+
+static void
+free_header(const header_type ht)
+{
+       int i;
+       
+       for (i = 0; i < MAX_HEADER_FIELDS; i++) {
+               char *c = header_lines[ht][i];
+               if (c != NULL) {
+                       xfree(c);
+                       header_lines[ht][i] = NULL;
+               }
+       }
+       header_ct[ht] = 0;
+       memset(header_fields[ht], 0, sizeof(header_fields[ht]));
+}
+
+/* data parsers */
+
+static void
+parse_position(const char *str, waypoint *wpt)
+{
+       double lat, lon;
+       unsigned char lathemi, hemilon;
+       int deg_lat, deg_lon, min_lat, min_lon;
+       
+       switch(grid_index) {
+               case 0:
+                       sscanf(str, "%c%lf %c%lf", &lathemi, &lat, &hemilon, &lon);
+                       break;
+               case 1:
+                       sscanf(str, "%c%d %lf %c%d %lf", &lathemi, &deg_lat, &lat, &hemilon, &deg_lon, &lon);
+                       lat = (double)deg_lat + (lat / (double)60);
+                       lon = (double)deg_lon + (lon / (double)60);
+                       break;
+               case 2:
+                       sscanf(str, "%c%d %d %lf %c%d %d %lf", &lathemi, &deg_lat, &min_lat, &lat, &hemilon, &deg_lon, &min_lon, &lon);
+                       lat = (double)deg_lat + ((double)min_lat / (double)60) + (lat / (double)3600.0);
+                       lon = (double)deg_lon + ((double)min_lon / (double)60) + (lon / (double)3600.0);
+                       break;
+       }
+       
+       if (lathemi == 'S') 
+               wpt->latitude = -lat;
+       else
+               wpt->latitude = lat;
+
+       if (hemilon == 'W')
+               wpt->longitude = -lon;
+       else
+               wpt->longitude = lon;
+}
+
+static int
+parse_distance(const char *str, double *value)
+{
+       double x;
+       char *buff;
+       
+       if ((str == NULL) || (*str == '\0')) return 0;
+       
+       buff = xmalloc(strlen(str) + 1);
+       sscanf(str, "%lf %s", &x, buff);
+       
+       if (case_ignore_strcmp(buff, "km") == 0) {
+               *value = x * (double)1000;
+       }
+       else if (case_ignore_strcmp(buff, "m") == 0) {          /* meters */
+               *value = x;
+       } 
+       else if (case_ignore_strcmp(buff, "ft") == 0) {         /* feet */
+               *value = x * (double)0.3048;
+       }
+       else if (case_ignore_strcmp(buff, "nm") == 0) {         /* mile (nautical / geographical) */
+               *value = x * (double)1852.0;
+       }
+       else if (case_ignore_strcmp(buff, "mi") == 0) {         /* mile (statute) */
+               *value = x * (double)1609.344;
+       }
+       else if (case_ignore_strcmp(buff, "fa") == 0) {         /* fathom */
+               *value = x * (double)1.8288;
+       }
+       else
+               fatal(MYNAME ": Unknown distance unit \"%s\" at line %d!\n", str, current_line);
+               
+       xfree(buff);
+       return 1;
+}
+
+static int
+parse_date_and_time(char *str, time_t *value)
+{
+       struct tm tm;
+       char *cerr, *cin;
+       
+       cin = lrtrim(str);
+       if (*cin == '\0') return 0;
+       
+       cerr = strptime(cin, date_time_format, &tm);
+       if (cerr == NULL) {
+               cerr = strptime(cin, "%m/%d/%Y %I:%M:%S %p", &tm);
+               is_fatal(cerr == NULL, MYNAME ": Invalid date or/and time \"%s\" at line %d!", cin, current_line);
+       }
+       
+//     printf(MYNAME "_parse_date_and_time: %02d.%02d.%04d, %02d:%02d:%02d\n",
+//             tm.tm_mday, tm.tm_mon+1, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+       *value = mktime(&tm);
+       return 1;
+}
+
+static gbuint16
+parse_categories(const char *str)
+{
+       char buff[256];
+       gbuint16 val;
+       gbuint16 res = 0;
+       char *cin, *cx;
+       
+       if (*str == '\0') return 0;
+       
+       strncpy(buff, str, sizeof(buff));
+       cin = lrtrim(buff);
+       if (*cin == '\0') return 0;
+       
+       strcat(cin, ",");
+       
+       while ((cx = strchr(cin, ','))) {
+               *cx++ = '\0';
+               cin = lrtrim(cin);
+               if (*cin != '\0') {
+                       if (!garmin_fs_convert_category(cin, &val)) 
+                               warning(MYNAME ": Unable to convert category \"%s\" at line %d!", cin, current_line);
+                       else
+                               res = res | val;
+               }
+               cin = cx;
+       }
+       return res;
+}
+
+static int
+parse_temperature(const char *str, double *temperature)
+{
+       double value;
+       unsigned char unit;
+       
+       if ((str == NULL) || (*str == '\0')) return 0;
+       
+       if (sscanf(str, "%lf %c", &value, &unit) == 2) {
+               unit = toupper(unit);
+               switch(unit) {
+                       case 'C': *temperature = value; break;
+                       case 'F': *temperature = (value - 32) / 1.8; break;
+                       default:
+                               fatal(MYNAME ": Unknown temperature unit \"%c\" at line %d!\n", unit, current_line);
+               }
+               return 1;
+       }
+       else
+               fatal(MYNAME ": Invalid temperature \"%s\" at line %d!\n", str, current_line);
+}
+
+static void
+parse_header(void)
+{
+       char *str;
+       int column = -1;
+       
+       free_header(unknown_header);
+       
+       while ((str = csv_lineparse(NULL, "\t", "", column++))) {
+               header_lines[unknown_header][column] = xstrdup(str);
+               str = header_lines[unknown_header][column];
+               while (*str) *str++ = toupper(*str);
+               header_ct[unknown_header]++;
+               if (header_ct[unknown_header] >= MAX_HEADER_FIELDS) break;
+       }
+}
+
+static int
+parse_display(const char *str, int *val)
+{
+       gt_display_modes_e i;
+       
+       if ((str == NULL) || (*str == '\0')) return 0;
+       
+       for (i = GT_DISPLAY_MODE_MIN; i <= GT_DISPLAY_MODE_MAX; i++) {
+               if (case_ignore_strcmp(str, gt_display_mode_names[i]) == 0) {
+                       *val = i;
+                       return 1;
+               }
+       }
+       warning(MYNAME ": Unknown display mode \"%s\" at line %d.\n", str, current_line);
+}
+
+static void
+bind_fields(const header_type ht)
+{
+       int i;
+       char *fields, *c;
+
+       is_fatal((grid_index < 0) || (datum_index < 0), MYNAME ": Incomplete or invalid file header!");
+               
+       if (header_ct[unknown_header] <= 0) return;
+       free_header(ht);
+       
+       /* make a copy of headers[ht], uppercase, replace "\t" with "\0" */
+       
+       i = strlen(headers[ht]);
+       fields = xmalloc(i + 2);
+       strcpy(fields, headers[ht]);
+       strcat(fields, "\t");
+       c = fields;
+       while (*c) *c++ = toupper(*c);
+       c = fields;
+       while ((c = strchr(c, '\t'))) *c++ = '\0';
+       
+       for (i = 0; i < header_ct[unknown_header]; i++) {
+               char *name;
+               int field_no;
+               name = header_lines[ht][i] = header_lines[unknown_header][i];
+               header_lines[unknown_header][i] = NULL;
+               
+               c = fields;
+               field_no = 1;
+               while (c != NULL) {
+                       if (strcmp(c, name) == 0) {
+                               header_fields[ht][i] = field_no;
+#if 0
+                               printf("Binding field \"%s\" to internal number %d (%d,%d)\n", name, field_no, ht, i);
+#endif
+                               break;
+                       }
+                       field_no++;
+                       c = c + strlen(c) + 1;
+               }
+       }
+       header_ct[unknown_header] = 0;
+       xfree(fields);
+}
+
+static void
+parse_grid(void)
+{
+       char *str = csv_lineparse(NULL, "\t", "", 1);
+       if (str != NULL) {
+               if (strstr(str, "dd.ddddd") != 0) grid_index = 0;
+               else if (strstr(str, "mm.mmm") != 0) grid_index = 1;
+               else if (strstr(str, "mm'ss.s") != 0) grid_index = 2;
+               else fatal(MYNAME ": Unsupported grid (%s)!\n", str);
+       }
+       else
+               fatal(MYNAME ": Missing grid headline!\n");
+}
+
+static void
+parse_datum(void)
+{
+       char *str = csv_lineparse(NULL, "\t", "", 1);
+       
+       if (str != NULL) {
+               datum_index = GPS_Lookup_Datum_Index(str);
+               is_fatal(datum_index < 0, MYNAME ": Unsupported GPS datum \"%s\"!", str);
+       }
+       else
+               fatal(MYNAME ": Missing GPS datum headline!\n");
+}
+
+static void
+parse_waypoint(void)
+{
+       char *str;
+       int column = -1;
+       waypoint *wpt;
+       garmin_fs_p gmsd = NULL;
+       
+       bind_fields(waypt_header);
+
+       wpt = waypt_new();
+       gmsd = garmin_fs_alloc(-1);
+       fs_chain_add(&wpt->fs, (format_specific_data *) gmsd);
+
+       while ((str = csv_lineparse(NULL, "\t", "", column++))) 
+       {
+               int i, dynamic;
+               double d;
+               int field_no = header_fields[waypt_header][column];
+               
+               switch(field_no) {
+                       case  1: wpt->shortname = DUPSTR(str); break;
+                       case  2: wpt->notes = DUPSTR(str); break;
+                       case  3:
+                               for (i = 0; i <= gt_waypt_class_map_line; i++) {
+                                       if (case_ignore_strcmp(str, gt_waypt_class_names[i]) == 0) {
+                                               GMSD_SET(wpt_class, i);
+                                               break;
+                                       }
+                               }
+                               break;
+                       case  4: parse_position(str, wpt); break;
+                       case  5: if (parse_distance(str, &d)) wpt->altitude = d; break;
+                       case  6: if (parse_distance(str, &d)) GMSD_SET(depth, d); break;
+                       case  7: if (parse_distance(str, &d)) GMSD_SET(proximity, d); break;
+                       case  8: if (parse_temperature(str, &d)) GMSD_SET(temperature, d); break;
+                       case  9: if (parse_display(str, &i)) GMSD_SET(display, i); break;
+                       case 10: break; /* skip color */
+                       case 11: 
+                               i = gt_find_icon_number_from_desc(str, GDB);
+                               GMSD_SET(icon, i);
+                               wpt->icon_descr = gt_find_desc_from_icon_number(i, GDB, &dynamic);
+                               wpt->wpt_flags.icon_descr_is_dynamic = dynamic;
+                               break;
+                       case 12: GMSD_SETSTR(facility, str); break;
+                       case 13: GMSD_SETSTR(city, str); break;
+                       case 14: GMSD_SETSTR(state, str); break;
+                       case 15: GMSD_SETSTR(cc, gt_get_icao_cc(str, wpt->shortname)); break;
+                       case 16: parse_date_and_time(str, &wpt->creation_time); break;
+                       case 17: wpt->url = DUPSTR(str); break;
+                       case 18: GMSD_SET(category, parse_categories(str)); break;
+                       default: break;
+               }
+       }
+       convert_datum(wpt, 1, NULL, NULL);
+       waypt_add(wpt);
+}
+
+static void
+parse_route_header(void)
+{
+       char *str;
+       int column = -1;
+       route_head *rte;
+       
+       rte = route_head_alloc();
+       
+       bind_fields(route_header);
+       while ((str = csv_lineparse(NULL, "\t", "", column++))) {
+               int field_no = header_fields[route_header][column];
+               switch(field_no) {
+                       case 1: rte->rte_name = DUPSTR(str); break;
+                       case 5: rte->rte_url = DUPSTR(str); break;
+               }
+       }
+       route_add_head(rte);
+       current_rte = rte;
+}
+
+static void
+parse_track_header(void)
+{
+       char *str;
+       int column = -1;
+       route_head *trk;
+       
+       bind_fields(track_header);
+       trk = route_head_alloc();
+       while ((str = csv_lineparse(NULL, "\t", "", column++))) {
+               int field_no = header_fields[track_header][column];
+               switch(field_no) {
+                       case 1: trk->rte_name = DUPSTR(str); break;
+                       case 6: trk->rte_url = DUPSTR(str); break;
+               }
+       }
+       track_add_head(trk);
+       current_trk = trk;
+}
+
+static void
+parse_route_waypoint(void)
+{
+       char *str;
+       int column = -1;
+       waypoint *wpt = NULL;
+       
+       bind_fields(rtept_header);
+       
+       while ((str = csv_lineparse(NULL, "\t", "", column++))) {
+               int field_no = header_fields[rtept_header][column];
+               switch(field_no) {
+                       case 1: 
+                               is_fatal((*str == '\0'), MYNAME ": Route waypoint without name at line %d!\n", current_line);
+                               wpt = find_waypt_by_name(str);
+                               is_fatal((wpt == NULL), MYNAME ": Route waypoint \"%s\" not in waypoint list (line %d)!\n", str, current_line);
+                               wpt = waypt_dupe(wpt);
+                               break;
+               }
+       }
+       if (wpt != NULL)
+               route_add_wpt(current_rte, wpt);
+}
+
+static void
+parse_track_waypoint(void)
+{
+       char *str;
+       int column = -1;
+       waypoint *wpt;
+       
+       bind_fields(trkpt_header);
+       wpt = waypt_new();
+       
+       while ((str = csv_lineparse(NULL, "\t", "", column++))) {
+               int field_no = header_fields[trkpt_header][column];
+               switch(field_no) {
+                       case 1: parse_position(str, wpt); break;
+                       case 2: parse_date_and_time(str, &wpt->creation_time); break;
+                       case 3: parse_distance(str, &wpt->altitude); break;
+               }
+       }
+       convert_datum(wpt, 1, NULL, NULL);
+       route_add_wpt(current_trk, wpt);
+}
+
+/***************************************************************/
+
+static void
+garmin_rd_init(const char *fname)
+{
+       memset(&gtxt_flags, 0, sizeof(gtxt_flags));
+       
+       fin = xfopen(fname, "rb", MYNAME);
+       memset(&header_ct, 0, sizeof(header_ct));
+
+       datum_index = -1;
+       grid_index = -1;
+       
+       init_date_and_time_format();
+}
+
+static void 
+garmin_rd_deinit(void)
+{
+       header_type h;
+
+       for (h = waypt_header; h <= unknown_header; h++) {
+               free_header(h);
+       }
+       fclose(fin);
+}
+
+static void
+garmin_txt_read(void)
+{
+       char buff[1024];
+
+       current_line = 0;
+
+       while ((fgets(buff, sizeof(buff), fin))) {
+               char *cin;
+               
+               current_line++;
+               cin = lrtrim(buff);
+               if (*cin == '\0') continue;
+               
+               cin = csv_lineparse(cin, "\t", "", 0);
+               
+               if (cin == NULL) continue;
+               else if (case_ignore_strcmp(cin, "Header") == 0) parse_header();
+               else if (case_ignore_strcmp(cin, "Grid") == 0) parse_grid();
+               else if (case_ignore_strcmp(cin, "Datum") == 0) parse_datum();
+               else if (case_ignore_strcmp(cin, "Waypoint") == 0) parse_waypoint();
+               else if (case_ignore_strcmp(cin, "Route Waypoint") == 0) parse_route_waypoint();
+               else if (case_ignore_strcmp(cin, "Trackpoint") == 0) parse_track_waypoint();
+               else if (case_ignore_strcmp(cin, "Route") == 0) parse_route_header();
+               else if (case_ignore_strcmp(cin, "Track") == 0) parse_track_header();
+               else if (case_ignore_strcmp(cin, "Map") == 0) /* do nothing */ ;
+               else
+                       fatal(MYNAME ": Unknwon identifier (%s) at line %d!\n", cin, current_line);
+                       
+               /* flush pending data */
+               while (csv_lineparse(NULL, "\t", "", 0));
+       }
+}
+
+ff_vecs_t garmin_txt_vecs = {
+       ff_type_file,
+       FF_CAP_RW_ALL,
+       garmin_rd_init, 
+       garmin_txt_wr_init,     
+       garmin_rd_deinit,       
+       garmin_txt_wr_deinit,   
+       garmin_txt_read,
+       garmin_txt_write,
+       NULL,
+       garmin_txt_args,
+       CET_CHARSET_MS_ANSI, 0
+};
diff --git a/gpsbabel/reference/garmin_txt.txt b/gpsbabel/reference/garmin_txt.txt
new file mode 100644 (file)
index 0000000..c868a96
--- /dev/null
@@ -0,0 +1,96 @@
+Grid   Lat/Lon hddd°mm.mmm'\r
+Datum  WGS 84\r
+\r
+Header Name    Description     Type    Position        Altitude        Depth   Proximity       Temperature     Display Mode    Color   Symbol  Facility        City    State   Country Date Modified   Link    Categories\r
+\r
+Waypoint       001             Map Line        N50 29.556188732 E12 06.325848140                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37             \r
+Waypoint       002             Map Intersection        N50 29.556188732 E12 06.325848140                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37             \r
+Waypoint       003             Map Intersection        N50 29.656610638 E12 06.307823695                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37             \r
+Waypoint       004             Map Line        N50 29.630036652 E12 06.366030984                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37             \r
+Waypoint       005             Map Line        N50 29.630036652 E12 06.366030984                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37             \r
+Waypoint       006             Map Intersection        N50 29.602537304 E12 06.426270045                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37             \r
+Waypoint       007             Map Line        N50 29.619586095 E12 06.429106481                                       Symbol  Unknown Waypoint                                        28/03/2006 00:10:37             \r
+Waypoint       ED_X    Dummy airport (Germany) Airport N51 53.627961650 E12 58.676564991                                       Symbol & Name   Unknown Airport FAC1    CITY1           Germany 28/03/2006 01:38:07             \r
+Waypoint       GC_X    Dummy airport (Spain)   Airport N38 37.919719778 W3 10.443304181                                        Symbol & Name   Unknown Airport FAC2    CITY2           Canary Island   28/03/2006 01:42:01             \r
+Waypoint       Jahnstrasse     Jahnstrasse 11  User Waypoint   N50 29.619998485 E12 06.429000869                                       Symbol & Description    Unknown Flag, Red                                       31/03/2006 21:48:22             \r
+Waypoint       LF_X    Dummy airport (France)  Airport N46 23.256332763 E3 29.896638617                                        Symbol & Name   Unknown Airport FAC3    CITY3           France  28/03/2006 01:40:32             \r
+Waypoint       Liebknechtstrasse       Liebknechtstrasse 90    User Waypoint   N50 29.630041681 E12 06.366015896                                       Symbol & Name   Unknown Waypoint                                        31/03/2006 21:49:30             \r
+Waypoint       LI_X    Dummy airport (Italy)   Airport N43 18.873018846 E12 09.693240859                                       Symbol & Name   Unknown Heliport        FAC4    CITY4           Italy   28/03/2006 01:43:25             \r
+Waypoint       NARVA   Start   User Waypoint   N50 29.556958191 E12 06.326884143       391 m                           Symbol  Unknown Flag, Green                                     31/03/2006 21:49:26     http://www.narva-light.de       Category 15\r
+\r
+\r
+Header Name    Length  Course  Waypoints       Link\r
+\r
+Route  ED_X-LF_X       4087 km 232° true       4 waypoints     \r
+\r
+Header Waypoint Name   Distance        Leg Length      Course\r
+\r
+Route Waypoint ED_X    0 m\r
+Route Waypoint GC_X    1936 km 1936 km 227° true\r
+Route Waypoint LI_X    3323 km 1388 km 63° true\r
+Route Waypoint LF_X    4087 km 764 km  300° true\r
+\r
+Route  NARVA to Jahnstrasse    394 m   46° true        10 waypoints    \r
+\r
+Header Waypoint Name   Distance        Leg Length      Course\r
+\r
+Route Waypoint NARVA   0 m\r
+Route Waypoint 001     2 m     2 m     221° true\r
+Route Waypoint 002     2 m     0 m     0° true\r
+Route Waypoint 003     189 m   188 m   353° true\r
+Route Waypoint 004     274 m   85 m    126° true\r
+Route Waypoint Liebknechtstrasse       274 m   0 m     298° true\r
+Route Waypoint 005     274 m   0 m     118° true\r
+Route Waypoint 006     362 m   88 m    126° true\r
+Route Waypoint 007     393 m   32 m    6° true\r
+Route Waypoint Jahnstrasse     394 m   1 m     351° true\r
+\r
+\r
+Header Name    Start Time      Elapsed Time    Length  Average Speed   Link\r
+\r
+Track  ACTIVE LOG 006  01/05/2005 15:02:47     0:33:09 653 m   1.2 kph \r
+\r
+Header Position        Time    Altitude        Depth   Leg Length      Leg Time        Leg Speed       Leg Course\r
+\r
+Trackpoint     N51 18.776294924 E12 24.789910130       01/05/2005 15:02:47     161 m   0.0 m\r
+Trackpoint     N51 18.772960603 E12 24.794909097       01/05/2005 15:03:25     154 m   0.0 m   8 m     0:00:38 0.8 kph 137° true\r
+Trackpoint     N51 18.771295957 E12 24.794909097       01/05/2005 15:03:39     148 m   0.0 m   3 m     0:00:14 0.8 kph 180° true\r
+Trackpoint     N51 18.769626282 E12 24.798243418       01/05/2005 15:04:16     139 m   0.0 m   5 m     0:00:37 0.5 kph 129° true\r
+Trackpoint     N51 18.769626282 E12 24.796573743       01/05/2005 15:05:02     145 m   0.0 m   2 m     0:00:46 0.2 kph 270° true\r
+Trackpoint     N51 18.769626282 E12 24.798243418       01/05/2005 15:05:45     134 m   0.0 m   2 m     0:00:43 0.2 kph 90° true\r
+Trackpoint     N51 18.766296990 E12 24.799908064       01/05/2005 15:06:44     131 m   0.0 m   6 m     0:00:59 0.4 kph 163° true\r
+Trackpoint     N51 18.766296990 E12 24.799908064       01/05/2005 15:07:50     130 m   0.0 m   0 m     0:01:06 0 kph   0° true\r
+Trackpoint     N51 18.764627315 E12 24.799908064       01/05/2005 15:08:19     132 m   0.0 m   3 m     0:00:29 0.4 kph 180° true\r
+Trackpoint     N51 18.767961636 E12 24.798243418       01/05/2005 15:11:16     144 m   0.0 m   6 m     0:02:57 0.1 kph 343° true\r
+Trackpoint     N51 18.774630278 E12 24.806576706       01/05/2005 15:12:34     147 m   0.0 m   16 m    0:01:18 0.7 kph 38° true\r
+Trackpoint     N51 18.779629245 E12 24.828242250       01/05/2005 15:13:18     145 m   0.0 m   27 m    0:00:44 2 kph   70° true\r
+Trackpoint     N51 18.782963566 E12 24.828242250       01/05/2005 15:13:27     145 m   0.0 m   6 m     0:00:09 2 kph   0° true\r
+Trackpoint     N51 18.782963566 E12 24.829906896       01/05/2005 15:13:37     135 m   0.0 m   2 m     0:00:10 0.7 kph 90° true\r
+Trackpoint     N51 18.786292858 E12 24.829906896       01/05/2005 15:13:46     135 m   0.0 m   6 m     0:00:09 2 kph   0° true\r
+Trackpoint     N51 18.792961501 E12 24.833241217       01/05/2005 15:14:03     136 m   0.0 m   13 m    0:00:17 3 kph   17° true\r
+Trackpoint     N51 18.797960468 E12 24.838240184       01/05/2005 15:14:16     135 m   0.0 m   11 m    0:00:13 3 kph   32° true\r
+Trackpoint     N51 18.796295822 E12 24.843239151       01/05/2005 15:14:26     139 m   0.0 m   7 m     0:00:10 2 kph   118° true\r
+Trackpoint     N51 18.796295822 E12 24.846573472       01/05/2005 15:14:30     139 m   0.0 m   4 m     0:00:04 3 kph   90° true\r
+Trackpoint     N51 18.782963566 E12 24.876572303       01/05/2005 15:15:06     141 m   0.0 m   43 m    0:00:36 4 kph   125° true\r
+Trackpoint     N51 18.777959570 E12 24.889909588       01/05/2005 15:15:27     140 m   0.0 m   18 m    0:00:21 3 kph   121° true\r
+Trackpoint     N51 18.774630278 E12 24.896573201       01/05/2005 15:15:39     140 m   0.0 m   10 m    0:00:12 3 kph   129° true\r
+Trackpoint     N51 18.776294924 E12 24.898242876       01/05/2005 15:25:31     152 m   0.0 m   4 m     0:09:52 0.0 kph 32° true\r
+Trackpoint     N51 18.776294924 E12 24.898242876       01/05/2005 15:25:40     152 m   0.0 m   0 m     0:00:09 0 kph   0° true\r
+Trackpoint     N51 18.777959570 E12 24.896573201       01/05/2005 15:29:18     155 m   0.0 m   4 m     0:03:38 0.1 kph 328° true\r
+Trackpoint     N51 18.789627180 E12 24.874907658       01/05/2005 15:30:30     149 m   0.0 m   33 m    0:01:12 2 kph   311° true\r
+Trackpoint     N51 18.789627180 E12 24.873243012       01/05/2005 15:30:37     150 m   0.0 m   2 m     0:00:07 1.0 kph 270° true\r
+Trackpoint     N51 18.789627180 E12 24.866574369       01/05/2005 15:30:47     151 m   0.0 m   8 m     0:00:10 3 kph   270° true\r
+Trackpoint     N51 18.789627180 E12 24.863240048       01/05/2005 15:30:48     151 m   0.0 m   4 m     0:00:01 14 kph  270° true\r
+Trackpoint     N51 18.799630143 E12 24.834905863       01/05/2005 15:30:52     150 m   0.0 m   38 m    0:00:04 34 kph  299° true\r
+Trackpoint     N51 18.821295686 E12 24.799908064       01/05/2005 15:30:57     150 m   0.0 m   57 m    0:00:05 41 kph  315° true\r
+Trackpoint     N51 18.839626908 E12 24.771573879       01/05/2005 15:31:03     150 m   0.0 m   47 m    0:00:06 28 kph  316° true\r
+Trackpoint     N51 18.852959163 E12 24.749908336       01/05/2005 15:31:10     150 m   0.0 m   35 m    0:00:07 18 kph  315° true\r
+Trackpoint     N51 18.877959028 E12 24.573239610       01/05/2005 15:32:38     143 m   0.0 m   210 m   0:01:28 9 kph   283° true\r
+Trackpoint     N51 18.877959028 E12 24.569910318       01/05/2005 15:32:45     141 m   0.0 m   4 m     0:00:07 2 kph   270° true\r
+Trackpoint     N51 18.877959028 E12 24.569910318       01/05/2005 15:33:17     143 m   0.0 m   0 m     0:00:32 0 kph   0° true\r
+Trackpoint     N51 18.877959028 E12 24.566575997       01/05/2005 15:33:42     139 m   0.0 m   4 m     0:00:25 0.6 kph 270° true\r
+Trackpoint     N51 18.877959028 E12 24.561572000       01/05/2005 15:33:54     139 m   0.0 m   6 m     0:00:12 2 kph   270° true\r
+Trackpoint     N51 18.877959028 E12 24.561572000       01/05/2005 15:34:04     138 m   0.0 m   0 m     0:00:10 0 kph   0° true\r
+Trackpoint     N51 18.877959028 E12 24.561572000       01/05/2005 15:34:20     139 m   0.0 m   0 m     0:00:16 0 kph   0° true\r
+Trackpoint     N51 18.877959028 E12 24.561572000       01/05/2005 15:35:45     144 m   0.0 m   0 m     0:01:25 0 kph   0° true\r
+Trackpoint     N51 18.877959028 E12 24.561572000       01/05/2005 15:35:56     145 m   0.0 m   0 m     0:00:11 0 kph   0° true\r
diff --git a/gpsbabel/reference/gdb-sample2.gdb b/gpsbabel/reference/gdb-sample2.gdb
new file mode 100644 (file)
index 0000000..670d0c5
Binary files /dev/null and b/gpsbabel/reference/gdb-sample2.gdb differ